package com.youzhi.backend.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youzhi.backend.dto.*;
import com.youzhi.backend.exception.EcpEnum;
import com.youzhi.backend.exception.YouZhiException;
import com.youzhi.backend.request.*;
import com.youzhi.backend.service.IWorkflowService;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.query.Query;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;

@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@Service
public class WorkflowServiceImpl implements IWorkflowService {


    /**
     * 说明：本来是不必要设置required = false的，但是在测试的时候，发现如果不设置使用PrivilegeInsertUtil工具的时候会创建一个
     * xml环境的applicationContext,这个环境不包含activiti相关对象，这个注入就会报错，所以这里设置为false，
     * 目的就是为了PrivilegeInsertUtil工具能够正常使用
     */
    @Autowired(required = false)
    private RepositoryService repositoryService;

    @Autowired(required = false)
    private RuntimeService runtimeService;

    @Autowired(required = false)
    private TaskService taskService;

    @Autowired(required = false)
    private HistoryService historyService;

    @Autowired(required = false)
    private IdentityService identityService;


    @Override
    public void deleteDeployment(DeleteDeploymentRequest request) {
        repositoryService.deleteDeployment(request.getDeploymentId(), request.isCascade());
    }


    /**
     * 查询部署的记录
     *
     * @param request
     * @return
     */
    @Override
    public IPage processesDeploymentPage(ProcessesDeploymentPageRequest request) {

        long total = repositoryService.createDeploymentQuery().count();

        List<ActivityDeployment> activityDeployments = listPage(repositoryService.createDeploymentQuery(), request, ActivityDeployment.class);

        return newPage(request, total, activityDeployments);

    }


    @Override
    public IPage processesDefinitionPage(ProcessesDefinitionPageRequest request) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery.latestVersion();
        long count = processDefinitionQuery.count();
        processDefinitionQuery.orderByProcessDefinitionId();
        processDefinitionQuery.desc();
        List<ActivityProcessDefinition> activityProcessDefinitions = listPage(processDefinitionQuery, request, ActivityProcessDefinition.class);
        return newPage(request, count, activityProcessDefinitions);
    }


    @Override
    public IPage processesInstancePage(ProcessesInstancePageRequest request) {

        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();
        processInstanceQuery.orderByProcessInstanceId();
        processInstanceQuery.desc();

        //通过反射的方式将,查询字段映射到查询条件
        warpCondition(processInstanceQuery, request);

        long count = processInstanceQuery.count();

        List<ActivityProcessInstance> activityProcessInstances = listPage(processInstanceQuery, request, ActivityProcessInstance.class);

        return newPage(request, count, activityProcessInstances);
    }

    @Override
    public IPage historyProcessesInstancePage(HistoryProcessesInstancePageRequest request) {

        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery();
        historicProcessInstanceQuery.orderByProcessInstanceEndTime();
        historicProcessInstanceQuery.desc();
        warpCondition(historicProcessInstanceQuery, request);

        long count = historicProcessInstanceQuery.count();

        List<ActivityHistoryProcessInstance> activityHistoryProcessInstances = listPage(historicProcessInstanceQuery, request, ActivityHistoryProcessInstance.class);

        return newPage(request, count, activityHistoryProcessInstances);
    }


    private void warpCondition(Object query, Object request) {

        Class<?> queryClass = query.getClass();

        Field[] declaredFields = request.getClass().getDeclaredFields();

        for (Field declaredField : declaredFields) {
            try {
                String name = declaredField.getName();
                declaredField.setAccessible(true);
                Object o = declaredField.get(request);
                if (o != null) {
                    Method declaredMethod = queryClass.getDeclaredMethod(name, declaredField.getType());
                    declaredMethod.invoke(query, o);
                }
            } catch (Exception e) {
                throw YouZhiException.newException(EcpEnum.unknown_error, e);
            }
        }


    }


    private <T> List<T> listPage(Query query, PageRequest pageRequest, Class<T> clazz) {
        Integer size = pageRequest.getSize();
        Integer current = pageRequest.getCurrent();
        List list = query.listPage((current - 1) * size, size);
        return (List<T>) list.stream().map(o -> BeanUtil.copyProperties(o, clazz)).collect(Collectors.toList());
    }


    private IPage newPage(PageRequest request, long total, List activityDeployments) {
        Page page = new Page();
        page.setSize(request.getSize());
        page.setCurrent(request.getCurrent());
        page.setTotal(total);
        page.setRecords(activityDeployments);
        return page;
    }


    @Override
    public void startProcessInstance(String processDefinitionKey) {
        try {
            identityService.setAuthenticatedUserId("admin");
            runtimeService.startProcessInstanceByKey(processDefinitionKey);
        } catch (Exception e) {
            throw YouZhiException.newException(EcpEnum.unknown_error, e);
        } finally {
            identityService.setAuthenticatedUserId(null);
        }

    }


    @Override
    public void deleteProcessInstance(DeleteProcessInstanceRequest request) {
        runtimeService.deleteProcessInstance(request.getProcessInstanceId(), request.getDeleteReason());
    }

    @Override
    public void suspendProcessInstance(SuspendProcessInstanceRequest request) {
        if (request.getIsSuspend()) {
            runtimeService.suspendProcessInstanceById(request.getProcessInstanceId());
        } else {
            runtimeService.activateProcessInstanceById(request.getProcessInstanceId());
        }
    }

    @Override
    public void deleteHistoryProcessInstance(String processInstanceId) {
        historyService.deleteHistoricProcessInstance(processInstanceId);
    }


}

